// ChCConnect.cpp : implementation file
//

#include "stdafx.h"
#include "ChatClient.h"
#include "ChCConnect.h"


// CChCConnect

CChCConnect* CChCConnect::s_pInstance = NULL;

CChCConnect::CChCConnect()
{
	m_bRun = FALSE;
	m_strUserName = _T("anonymus");
}

CChCConnect::~CChCConnect()
{
	m_strUserName.FreeExtra();
}

CChCConnect* CChCConnect::Instance()
{
	if(NULL == s_pInstance)
	{
		s_pInstance = new CChCConnect;

		if(FALSE == s_pInstance->m_bRun)
		{
			s_pInstance->m_bRun = TRUE;
			BOOL bRet = s_pInstance->Create();
		}
	}

	return s_pInstance;
}

BOOL CChCConnect::Initialize(const CString& strUserName)
{	
	if(NULL == Instance())
		return FALSE;

	Instance()->UserName(strUserName);

	return TRUE;
}

void CChCConnect::Uninitialize()
{
	CString strBye(_T("bye"));
	Instance()->Send((LPCTSTR)strBye,(strBye.GetLength()+1)*sizeof(TCHAR));
	Instance()->ShutDown();
	Instance()->Close();
	s_pInstance->m_bRun = FALSE;

	delete s_pInstance;
	s_pInstance = NULL;	
}

void CChCConnect::OnReceive(int nErrorCode)
{
	static int i = 0;
	i++;

	TCHAR buff[4096];
	int nRead;
	nRead = Receive(buff, 4096); 

	switch (nRead)
	{
	case 0:
		Close();
		break;
	case SOCKET_ERROR:
		if (GetLastError() != WSAEWOULDBLOCK) 
		{
			AfxMessageBox (_T("Error occurred"));
			Close();
		}
		break;
	default:
		buff[nRead] = _T('\0'); //terminate the string

		//if this is message with new user name
		CString strMsg(buff);
		int nPos = strMsg.Find(NEW_USER_MESSAGE_PREFIX);
		if(nPos >= 0)
		{
			CString strNewUser = strMsg.Right(strMsg.GetLength() - nPos - NEW_USER_MESSAGE_PREFIX.GetLength());
			if(strNewUser != m_strUserName)
			{
				m_UsersArr.AddTail(strNewUser);

				//notify about new user
				CString strMsg;
				strMsg.AppendFormat(_T("System: user %s connected"), strNewUser);
				AfxGetApp()->GetMainWnd()->SendMessage(CHM_ADDMESSAGE,nRead,(LPARAM)strMsg.GetBuffer());
				AfxGetApp()->GetMainWnd()->SendMessage(CHM_SERVER_ADD_USER,nRead,(LPARAM)strNewUser.GetBuffer());
			}
			CAsyncSocket::OnReceive(nErrorCode);
			return;
		}

		//if this is message with remove user name
		nPos = strMsg.Find(REMOVE_USER_MESSAGE_PREFIX);
		if(nPos >= 0)
		{
			CString strNewUser = strMsg.Right(strMsg.GetLength() - nPos - REMOVE_USER_MESSAGE_PREFIX.GetLength());
			if(strNewUser != m_strUserName)
			{
				POSITION pos = m_UsersArr.Find(strNewUser);
				if(NULL != pos)
					m_UsersArr.RemoveAt(pos);

				//notify about remove user
				CString strMsg;
				strMsg.AppendFormat(_T("System: user %s disconnected"), strNewUser);
				AfxGetApp()->GetMainWnd()->SendMessage(CHM_ADDMESSAGE,nRead,(LPARAM)strMsg.GetBuffer());
				AfxGetApp()->GetMainWnd()->SendMessage(CHM_SERVER_REMOVE_USER,nRead,(LPARAM)strNewUser.GetBuffer());
			}
			CAsyncSocket::OnReceive(nErrorCode);
			return;
		}
		
		if (strMsg.CompareNoCase(_T("bye")) == 0)
		{
			ShutDown();
			AfxGetApp()->GetMainWnd()->SendMessage(CHM_SERVER_CLOSE_CONNECT,0,0);
			CAsyncSocket::OnReceive(nErrorCode);
			return;
		}

		if (strMsg.CompareNoCase(_T("test")) == 0)
		{
			CAsyncSocket::OnReceive(nErrorCode);
			return;
		}

		if (strMsg.CompareNoCase(_T("badlogin")) == 0)
		{
			ShutDown();
			AfxGetApp()->GetMainWnd()->SendMessage(CHM_SERVER_CLOSE_CONNECT,0,0);
			AfxGetApp()->GetMainWnd()->SendMessage(CHM_SERVER_BAD_LOGIN,0,0);
			CAsyncSocket::OnReceive(nErrorCode);
			return;
		}

		//this is simple message
		AfxGetApp()->GetMainWnd()->SendMessage(CHM_ADDMESSAGE,nRead,(LPARAM)buff);
	}
	
	CAsyncSocket::OnReceive(nErrorCode);
}

void CChCConnect::OnConnect(int nErrorCode)
{
	if (0 != nErrorCode)
	{
		switch(nErrorCode)
		{
		case WSAEADDRINUSE: 
			AfxMessageBox(_T("The specified address is already in use.\n"));
			break;
		case WSAEADDRNOTAVAIL: 
			AfxMessageBox(_T("The specified address is not available from ")
				_T("the local machine.\n"));
			break;
		case WSAEAFNOSUPPORT: 
			AfxMessageBox(_T("Addresses in the specified family cannot be ")
				_T("used with this socket.\n"));
			break;
		case WSAECONNREFUSED: 
			AfxMessageBox(_T("The attempt to connect was forcefully rejected.\n"));
			break;
		case WSAEDESTADDRREQ: 
			AfxMessageBox(_T("A destination address is required.\n"));
			break;
		case WSAEFAULT: 
			AfxMessageBox(_T("The lpSockAddrLen argument is incorrect.\n"));
			break;
		case WSAEINVAL: 
			AfxMessageBox(_T("The socket is already bound to an address.\n"));
			break;
		case WSAEISCONN: 
			AfxMessageBox(_T("The socket is already connected.\n"));
			break;
		case WSAEMFILE: 
			AfxMessageBox(_T("No more file descriptors are available.\n"));
			break;
		case WSAENETUNREACH: 
			AfxMessageBox(_T("The network cannot be reached from this host ")
				_T("at this time.\n"));
			break;
		case WSAENOBUFS: 
			AfxMessageBox(_T("No buffer space is available. The socket ")
				_T("cannot be connected.\n"));
			break;
		case WSAENOTCONN: 
			AfxMessageBox(_T("The socket is not connected.\n"));
			break;
		case WSAENOTSOCK: 
			AfxMessageBox(_T("The descriptor is a file, not a socket.\n"));
			break;
		case WSAETIMEDOUT: 
			AfxMessageBox(_T("The attempt to connect timed out without ")
				_T("establishing a connection. \n"));
			break;
		default:
			TCHAR szError[256];
			_stprintf_s(szError, _T("OnConnect error: %d"), nErrorCode);
			AfxMessageBox(szError);
			break;
		}
	}
	else
	{
		AfxGetApp()->GetMainWnd()->SendMessage(CHM_CONNECTED,0,0);
	}
	CAsyncSocket::OnConnect(nErrorCode);

}

void CChCConnect::OnSend(int nErrorCode)
{
	CString& strMsg = Instance()->UserName();
	Send((LPCTSTR)strMsg,(strMsg.GetLength()+1)*sizeof(TCHAR));

	CAsyncSocket::OnSend(nErrorCode);
}
